<?php
 class OGC{
    
    # definition of operations and parameters, template variables would be initialised from the class
    # that implements OGC
    protected $allowed_operations = 
        array( 'GetCapabilities' => 
          array( 'template' => '/route/to/default/template',
                 'params' => array (
                    'service'  => array( 'route' => "//*[local-name()='GetCapabilities']/@service",
                                         'value' => '' ) )
               )
             );
    # exception array
    protected $exception;
    
    # substitution parameters for the template
    protected $parameters = null;

    # name of current operation
    protected $operationname = '';

    # reference into $allowed_operations for the current operation
    protected $operationref = null;

    # the template for the XML based reply - loaded in setResponse()
    protected $reply_template = '';

    # the value of the service parameter, depends on the operation, usually set by 
    # extending class constructor
    protected $service = 'OGC';

    # some parameters to allow flexible substitutions
    protected $subs_pre = '\$\$';           # prepend to substitution name
    protected $subs_app = '\$\$';           # append to substitution name
    protected $subs_cap  = CASE_UPPER;      # change substitution name to given case

    # static exported methods
    
    # returns true is method is post and the raw post data looks like xml
    # it does not check validity of xml
    static public function isXMLRequest()
    {     
        if ($_SERVER['REQUEST_METHOD'] != 'POST' )
            return false;
        # allow to start with whitespace ?        
        if ( strpos( $GLOBALS['HTTP_RAW_POST_DATA'], '<?xml' ) !== 0 )
            return false;
        return true;
    }

    # returns true if there is something in $_REQUEST, method is not checked as it
    # can be both GET/POST
    static public function isKVPRequest()
    {
        if ( empty($_REQUEST) )
            return false;
        return true;
    }

    # non static methods
    
    # Susbtitute fields into the reply_template, gets called for both 
    # $allowed_operations and $parameters
    private function _substitute( $params )
    {
        if(sizeof($params)>0){
            foreach ( $params as $key => $value )
            {
                if ( $this->subs_cap == CASE_UPPER )
                    $key = strtoupper($key);
                else
                    $key = strtolower($key);

                if ( is_array( $value ) )
                    $value = $value['value'];

                $this->reply_template = preg_replace( '/' . $this->subs_pre . $key . $this->subs_app . '/', 
                    $value, $this->reply_template );
            }
        }
    }

    # Obtains the parameter values of the current operation from the SimpleXML object
    # Apart from the requested operation, no input is validated.
    protected function getXMLParams( $xml )
    {     
         if ( get_class($xml) != 'SimpleXMLElement' ){
            $this->exception[]= array('code'=>'InvalidParameterValue','message'=>'Invalid parameter in getXMLParams(' . get_class($xml) .')' );
            return false;
         }

        # first recover the requested operation
        
        if ( in_array( $xml->getName(), array_keys( $this->allowed_operations ), TRUE ) === FALSE ){
            $this->exception[]= array('code'=>'OperationNotSupported','message'=>'Illegal operation.');
            return false;
        }

        $this->operationname = $xml->getName();
        $this->operationref  = &$this->allowed_operations[$this->operationname];
        
        if ( is_null( $this->operationref ) ){
            $this->exception[]= array('code'=>'OperationNotSupported','message'=>'Illegal operation ('.$this->operationname.')');
            return false;
        }

        # find the corresponding parameters - none of the parameters are validated at this point
        foreach ( array_keys( $this->operationref['params'] ) as $key )
        {
            $sel = $xml->xpath( $this->operationref['params'][$key]['route'] );
            if ( $sel === FALSE ){
                $this->exception[]= array('code'=>'InvalidParameterValue','message'=>"XML Element $key was not found." );
                return false; 
            }
 
            $this->operationref['params'][$key]['value'] = (string)$sel[0];
//            print "$key = " . $this->operationref['params'][$key]['value'] . "\n";
        } 
    }

    # Obtains the parameter values of the current operation from a POST/GET request
    # Apart from the requested operation, no input is validated.
    protected function getKVPParams( $array )
    {
        if ( !is_array($array) ){
            $this->exception[]= array('code'=>'InvalidParameterValue','message'=>'Invalid parameter in getKVPParams(' . gettype($xml) .')' );
            return false;
        }

        # first convert the key names to lower case
        $input = array_change_key_case( $array, CASE_LOWER );    

        if ( !isset( $input['request'] ) ){
            $this->exception[]= array('code'=>'MissingParameterValue','message'=>'No operation requested.');
            return false;
        }

        if ( in_array( $input['request'], array_keys( $this->allowed_operations ), TRUE ) === FALSE ){
            $this->exception[]= array('code'=>'OperationNotSupported','message'=>'Illegal operation ('.$this->operationname.')');
            return false;
        }

        $this->operationname = $input['request'];
        $this->operationref  = &$this->allowed_operations[$this->operationname];
        
        if ( is_null( $this->operationref ) ){
            $this->exception[]= array('code'=>'OperationNotSupported','message'=>'Illegal operation ('.$this->operationname.')');
            return false;
        }

        # find the corresponding parameters - none of the parameters are validated at this point
        foreach ( array_keys( $this->operationref['params'] ) as $key )
        {
            if ( !isset( $input[strtolower($key)] ) )
                continue;
            
            $this->operationref['params'][$key]['value'] = $input[strtolower($key)];
//            print "$key = " . $this->operationref['params'][$key]['value'] . "\n";
        } 
    }

    ###
    ##test 
    function getCapabilities(){
        return $this->reply_template;
    }    


    # public functions
     
    # Susbtitute fields from both $allowed_operations and $parameters
    # into the $reply_template
    public function substitute()
    {
        $this->_substitute( $this->parameters );
        $this->_substitute( $this->operationref['params'] );
    }


    # Sets the service that is being offered by this implementation
    # Usually initialised from a extending constructor
    public function setService( $value )
    {
        $this->service = strtoupper($value);
    }

    # Checks if the service field of the requested operation was set correctly
    # This is called from setResponse()
    public function checkService()
    {
        if ( $this->operationref == null )
            return false;

        if ( strtoupper($this->operationref['params']['service']['value']) != $this->service )
            return false;

        return true;
    }


    #This function chooses the correct function for a given operation name -> variable functions
    function setOperationMethod($operationname){
        if(method_exists($this,$operationname))
            return $this->$operationname();
        else {
         $this->exception[]= array('code'=>'OperationNotSupported','message'=>'Operation does not exist');
         return false;
         }
    }
    
    #This function should read the Allowed_Operations XML and set the metadata (parameters) for the given operation
    function setOperationMetadata($operation){
        #read the XML and choose the correct params
        $this->operationparams=$params;
    }
    
    #Parses the incoming request
    public function getRequest( $input ){
        $err = '';
        
        if ( is_array( $input ) )
        {
            $err = $this->getKVPParams( $input );
        }
        elseif ( is_string($input) )
        {
            libxml_use_internal_errors(true);
            $xml = simplexml_load_string( $input);

            if ( $xml ===FALSE ){
                $this->exception[]= array('code'=>'InvalidParameterValue','message'=>'invalid xml');
                return false;
            }
            $err = $this->getXMLParams($xml);
        }
        else
        {
            $this->exception[]= array('code'=>'InvalidParameterValue','message'=>'Unknown input argument type.');
            return false;
        }
#        $this->operationname=$foundopname;
#        $this->operationparams=$this->setOperationMetadata($this->operationname);

        return $err;
    }

    function getException(){
		$xml='<?xml version="1.0" encoding="UTF-8"?><ExceptionReport xmlns="http://www.opengis.net/ows/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/ows/2.0 owsExceptionReport.xsd" version="1.0.0" xml:lang="en">';
			if(sizeof($this->exception)>0){
			foreach($this->exception as $exc)
				$xml.='<Exception exceptionCode="'.$exc['code'].'" locator="service">'.$exc['message'].'</Exception>';
			}
			$xml.='</ExceptionReport>';
		return $xml;
	}
    
    public function setResponse(){

        try {
            if ( $this->operationref == null ){
                $this->exception[]= array('code'=>'MissingParameterValue','message'=>'No operation has been specified.');
                throw new Exception('Invalid OGC parameter');
            }

            if ( $this->checkService() === false ){
                $this->exception[]= array('code'=>'InvalidParameterValue','message'=>'Invalid service requested, I only provide '.$this->service);
                throw new Exception('Invalid OGC parameter');
            }

            if ( ($this->reply_template = @file_get_contents( $this->operationref['template'])) === FALSE ){
                $this->exception[]= array('code'=>'NoApplicable Code','message'=>'Cannot load template.');
                throw new Exception('Invalid OGC parameter');
            }
            
            if ( $this->setOperationMethod($this->operationname) == false )
                throw new Exception('OGC operation failed to execute.');

            $this->substitute();
        } catch ( Exception $exc )
        {
			return $this->getException();
        }

        return $this->reply_template;
    }    

    # Completely handles an request, returning the XML response 
    function handleRequest() {
        if ( OGC::isXMLRequest() === true )
        {    
            $this->getRequest($GLOBALS['HTTP_RAW_POST_DATA']); 
        }
        elseif ( OGC::isKVPRequest() === true )
        {
            $this->getRequest( $_REQUEST );
        }

         return $this->setResponse();
    }


    function __construct (){}  
    function __destruct () {}
 }
 
# $o=new OGC();
# echo $o->setOperationMethod('getCapabilities');
# echo $o->setOperationMethod('getObservation');
#echo $o->getRequest();
?>
